package com.bright.hxj.cwduijie.service.impl;

import cn.hutool.core.util.ReUtil;
import com.bright.ghj.common.util.ListUtil;
import com.bright.ghj.common.util.StringUtil;
import com.bright.hxj.cwduijie.constant.DBTypeConstants;
import com.bright.hxj.cwduijie.enums.DataApi;
import com.bright.hxj.cwduijie.enums.OpType;
import com.bright.hxj.cwduijie.enums.SJJHBZ;
import com.bright.hxj.cwduijie.pojo.bo.ApiDataCollection;
import com.bright.hxj.cwduijie.pojo.command.ExecuteUpdateSqlCommand;
import com.bright.hxj.cwduijie.pojo.dto.CheckResultDto;
import com.bright.hxj.cwduijie.pojo.po.AssetsDraw;
import com.bright.hxj.cwduijie.pojo.query.DuijieQuery;
import com.bright.hxj.cwduijie.repository.AssetsDrawRepository;
import com.bright.hxj.cwduijie.service.AbstractPerThousandApiService;
import com.bright.hxj.cwduijie.util.EntityManagerUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Component;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * @Author hxj
 * @Date 2024/11/21 15:36
 * @Description
 */
@Component
@RequiredArgsConstructor
public class ApiAssetsDrawServiceImpl extends AbstractPerThousandApiService<AssetsDraw> {

    private final AssetsDrawRepository repo;

    @Override
    public JpaSpecificationExecutor getJpaSpecificationExecutor() {
        return repo;
    }

    @Override
    public DataApi getDataApi() {
        return DataApi.ASSETS_DRAW;
    }

    @Override
    public Function<AssetsDraw, Integer> getIdFunc() {
        return AssetsDraw::getID;
    }

    @Override
    public Function<AssetsDraw, String> getDataKeyFunc() {
        return AssetsDraw::getDATA_KEY;
    }

    @Override
    public Function<ApiDataCollection, List<AssetsDraw>> getDataFunc() {
        return ApiDataCollection::getAssetsDraws;
    }

    @Override
    public DataApi getDeleteDataApi() {
        return DataApi.DELETE_ASSETS_DRAW;
    }


    @Override
    protected void getData(ApiDataCollection apiDataCollection, DuijieQuery duijieQuery) {
//        List<BioAssets> assets = repo.findAll(getDataQuerySpecification(OpType.ADD_OR_EDIT.getValue(), duijieQuery.getCreditCode()));
        Map<String, List<AssetsDraw>> assetsDraws = getAssetsDrawGroupByAssetDataKey(duijieQuery);
        List<AssetsDraw> result = new ArrayList<>();
        // v1.6.19 处理一个资产编号对应多个POLYGON数据的情况 将多个POLYGON合成一个MULTIPOLYGON
        for (Map.Entry<String, List<AssetsDraw>> entry : assetsDraws.entrySet()) {
            List<AssetsDraw> assetsDrawList = entry.getValue();
            // 如果这个资产只有一个 则不做处理
            boolean isNotProcess = assetsDrawList.size() <= 1;

            // 先检查是否每个都是POLYGON 如果有MULTIPOLYGON 也不处理
//            for (AssetsDraw assetsDraw : assetsDrawList) {
//                String range = StringUtil.trimToEmpty(assetsDraw.getRANGE());
//                if (!StringUtil.startsWith(range, "POLYGON")) {
//                    isNotProcess = true;
//                    break;
//                }
//            }

            if (isNotProcess) {
                result.addAll(assetsDrawList);
            } else {
                List<String> polygons = ListUtil.toSingleList(assetsDrawList, AssetsDraw::getRANGE);
                String multiPolygonString = getMultiPolygonStringByPolygons(polygons);
                // 取第一个
                AssetsDraw assetsDraw = assetsDrawList.get(0);
                assetsDraw.setRANGE(multiPolygonString);
                result.add(assetsDraw);
                // 更新到数据表

                String updateSql = "update DJ_ZCMTSJ set range=:range where id=:id ; delete from DJ_ZCMTSJ where id in (:deleteIds) ;";
                Map<String, Object> params = new HashMap<>(2);
                params.put("range", multiPolygonString);
                params.put("id", assetsDraw.getID());
                // 不是第一个的 进行删除
                List<Integer> needDeleteIds = new ArrayList<>();
                for (int i = 1; i < assetsDrawList.size(); i++) {
                    needDeleteIds.add(assetsDrawList.get(i).getID());
                }
                params.put("deleteIds", needDeleteIds);
                duijieManager.executeUpdateSql(new ExecuteUpdateSqlCommand(updateSql, params));
            }
        }
        apiDataCollection.setAssetsDraws(result);
    }

    private Map<String, List<AssetsDraw>> getAssetsDrawGroupByAssetDataKey(DuijieQuery duijieQuery) {
        Map<String, List<AssetsDraw>> result = new HashMap<>();
        List<AssetsDraw> all = repo.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> ps = new ArrayList<>();

            String ztFieldName = isExistsTYSHXYDMField() ? "TYSHXYDM" : "ZT_DATA_KEY";

            ps.add(criteriaBuilder.equal(root.get(ztFieldName), duijieQuery.getCreditCode()));
            ps.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.WAITING.getValue()),
                    criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.ERROR.getValue())));
            ps.add(criteriaBuilder.equal(root.get("opType"), OpType.ADD_OR_EDIT.getValue()));

            criteriaQuery.where(ps.toArray(new Predicate[0]));
            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("ASSET_DATA_KEY")));
            return criteriaQuery.getRestriction();
        });
        for (AssetsDraw assetsDraw : all) {
            result.putIfAbsent(assetsDraw.getASSET_DATA_KEY(), new ArrayList<>());
            result.get(assetsDraw.getASSET_DATA_KEY()).add(assetsDraw);
        }
        return result;
    }

    private static final Pattern POLYGON_PATTERN_WHOLE = Pattern.compile("POLYGON\\s*\\(\\((\\s*-?\\d+(\\.\\d+)?\\s* \\s*-?\\d+(\\.\\d+)?\\s*,?)+\\)\\)"),
            MULTIPOLYGON_PATTERN_WHOLE = Pattern.compile("MULTIPOLYGON\\s*\\((\\(\\((\\s*-?\\d+(\\.\\d+)?\\s* \\s*-?\\d+(\\.\\d+)?\\s*,?)+\\)\\),?\\s*)+\\)"),
            POLYGON_PATTERN_EACH_POLYGON = Pattern.compile("\\(\\((\\s*-?\\d+(\\.\\d+)?\\s* \\s*-?\\d+(\\.\\d+)?\\s*,?)+\\)\\)"),
            POINT_PATTERN = Pattern.compile("(-?\\d+(\\.\\d+)?\\s* \\s*-?\\d+(\\.\\d+)?\\s*)+");
    // 判断两个面是否重叠的语句
    private static final String SQLSERVER_CHECK_POLYGON_OVERLAP_SQL = "DECLARE @g geometry;  \n" +
            "DECLARE @h geometry;  \n" +
            "SET @g = geometry\\:\\:STGeomFromText(:polygonA, 4326)\n" +
            "SET @h =  geometry\\:\\:STGeomFromText(:polygonB, 4326)\n" +
            "SELECT case when @g.STContains(@h)=1 or @g.STOverlaps(@h)=1 then 1 else 0 end as isCross;";
    private static final String DM_CHECK_POLYGON_OVERLAP_SQL = "DECLARE v_polygonA sysgeo.ST_POLYGON; v_polygonB sysgeo.ST_POLYGON;\n" +
            "begin  \n" +
            "SET v_polygonA = dmgeo.ST_PolyFromText(:polygonA, 0);\n" +
            "SET v_polygonB = dmgeo.ST_PolyFromText(:polygonB, 0);\n" +
            "SELECT case when dmgeo.ST_CONTAINS(v_polygonA, v_polygonB)=1 or dmgeo.ST_INTERSECTS(v_polygonA, v_polygonB)=1 or dmgeo.ST_OVERLAPS(v_polygonA, v_polygonB)=1 then 1 else 0 end as isCross;\n" +
            "end;";
    private static final String CHECK_POLYGON_OVERLAP_SQL = EntityManagerUtil.IS_DM ? DM_CHECK_POLYGON_OVERLAP_SQL : SQLSERVER_CHECK_POLYGON_OVERLAP_SQL;
    private String getMultiPolygonStringByPolygons(List<String> polygons) {
        // v1.7.2 去掉重复的面数据
        Set<String> polygonSet = new LinkedHashSet<>(polygons.size());
        for (String polygon : polygons) {
            polygonSet.addAll(ReUtil.findAllGroup0(POLYGON_PATTERN_EACH_POLYGON, polygon));
        }

        List<String> polygonList = new ArrayList<>(polygonSet), afterPolygonList = new ArrayList<>(polygonSet.size());
        // v1.7.4 判断面与面之间是否有重叠 如果有重叠就只推第一个面
        for (int i = 0; i < polygonList.size(); i++) {
            String polygonA = polygonList.get(i);
            boolean isCross = false;
            for (int j = i + 1; j < polygonList.size(); j++) {
                String polygonB = polygonList.get(j);
                Map<String, Object> params = new HashMap<>(2);
                params.put("polygonA", "POLYGON " + polygonA);
                params.put("polygonB", "POLYGON " + polygonB);
                List<Map<String, Object>> maps = EntityManagerUtil.queryForMapList(entityManager, CHECK_POLYGON_OVERLAP_SQL, params);
                Integer isCrossInt = (Integer) maps.get(0).get("isCross");
                isCross = isCrossInt == 1;
                if (isCross) {
                    break;
                }
            }
            afterPolygonList.add(polygonA);
            if (isCross) {
                // 存在重叠 只推到这一条
                break;
            }
        }

        StringBuilder result = new StringBuilder("MULTIPOLYGON (");
        boolean isFirst = true;
        for (String p : afterPolygonList) {
            if (isFirst) {
                isFirst = false;
            } else {
                result.append(", ");
            }
            result.append(p);
        }

        result.append(")");
        return result.toString();
    }

    @Override
    protected Long getCheckDuplicateCount(String groupByFields, DataApi api) {
        // v1.7.4 当一张图数据库给同一笔资产画多条描边数据，DJ_ZCMTSJ的DATA_KEY会重复，这里改成查同一个DATA_KEY但有不同ASSET_DATA_KEY的情况
        return EntityManagerUtil.queryForCount(entityManager,
                "SELECT COUNT(*) FROM DJ_ZCMTSJ WHERE DATA_KEY IN (\n" +
                        "    SELECT DATA_KEY FROM DJ_ZCMTSJ where isnull(opType,1)=1 GROUP BY DATA_KEY HAVING COUNT(DISTINCT ASSET_DATA_KEY) > 1\n" +
                        ")",
                Collections.emptyMap());
    }
}
